#include "StdAfx.h"
#include "NetBase.h"

#include <Mswsock.h>

SIZE_T CNetBase::IO_INFO::sizeSelf=sizeof(CNetBase::IO_INFO);
SIZE_T CNetBase::IO_DATA::sizeSelf=sizeof(CNetBase::IO_DATA);
SIZE_T CNetBase::TransferData::sizeSelf=sizeof(CNetBase::TransferData);
CNetBase::CNetBase(void)
{
	m_hIOCP=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
	AfxBeginThread(&CNetBase::WordThread,this);
	ASSERT(m_hIOCP!=NULL);
	m_hMutex=CreateMutex(NULL,FALSE,NULL);
}

CNetBase::~CNetBase(void)
{
	CloseHandle(m_hIOCP);
	CloseHandle(m_hMutex);
}

SOCKET CNetBase::Connect(const char *ulIPAddr, WORD wPort)
{
	return CNetBase::Connect(inet_addr(ulIPAddr),wPort);
}

SOCKET CNetBase::Connect(ULONG ulAddr, WORD wPort)
{
// 	WSAPROTOCOL_INFO wsaPI;
// 	INT iProtocol=IPPROTO_TCP;
// 	DWORD dwBufferLength=sizeof(wsaPI);
// 	WSAEnumProtocols(&iProtocol,&wsaPI,&dwBufferLength);
	SOCKET sock=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,NULL,WSA_FLAG_OVERLAPPED);
	if(INVALID_SOCKET==sock)
	{
		return sock;
	}
	LPTransferData lpTransferData=TransferData::NewData();
	m_ioMAP[sock]=lpTransferData;

	sockaddr_in saRemoteAddr,saNullAddr;
	ZeroMemory(&saRemoteAddr,sizeof(sockaddr_in));
	ZeroMemory(&saNullAddr,sizeof(sockaddr_in));
	saRemoteAddr.sin_family=AF_INET;
	saRemoteAddr.sin_port=htons(wPort);
	saRemoteAddr.sin_addr.s_addr=ulAddr;
	saNullAddr.sin_family=AF_INET;
	
	bind(sock,(const sockaddr*)&saNullAddr,sizeof(sockaddr));
	lpTransferData->GetSocket()=sock;
	lpTransferData->GetAddr()=saRemoteAddr;
	HANDLE hIOCP=CreateIoCompletionPort((HANDLE)lpTransferData->GetSocket(),m_hIOCP,(ULONG_PTR)lpTransferData,0);
	DWORD dwErr=GetLastError();
	
	ASSERT(hIOCP==m_hIOCP);
	
	LPFN_CONNECTEX ConnectEx=NULL;
	GUID guidConnectex=WSAID_CONNECTEX;
	DWORD dwByteReturned;
	WSAIoctl(sock,
		SIO_GET_EXTENSION_FUNCTION_POINTER,
		&guidConnectex,
		sizeof(GUID),
		&ConnectEx,
		sizeof(ConnectEx),
		&dwByteReturned,
		NULL,
		NULL
		);
	if(ConnectEx)
	{
		//PIO_DATA pData=IO_DATA::NewData(IO_DATA::OP_CONNECTING);
		DWORD dwBytesSent;
		lpTransferData->SetConnecting();
		ConnectEx(sock,(const sockaddr*)&saRemoteAddr,sizeof(sockaddr),NULL,0,&dwBytesSent,(LPOVERLAPPED)lpTransferData->GetConnectData());
		DWORD dwErr=GetLastError();
		int i=0;
	}
	return sock;
}

UINT CNetBase::WordThread(LPVOID lpParam)
{
	CNetBase *pThis=static_cast<CNetBase*>(lpParam);
	if(!pThis)
	{
		return -1;
	}
	while(TRUE)
	{
		DWORD dwNumberOfByteTransferred;
		PIO_DATA pData;
		LPTransferData pTransferData;
		if(GetQueuedCompletionStatus(pThis->m_hIOCP,&dwNumberOfByteTransferred,(PULONG_PTR)&pTransferData,(LPOVERLAPPED*)&pData,WSA_INFINITE))
		{
			switch(pData->GetStatus())
			{
			case IO_DATA::OP_CONNECT:
				{
					pTransferData->ClearConnecting();
					pThis->OnConnect(pTransferData,pData,dwNumberOfByteTransferred);
				}
				break;
			case IO_DATA::OP_SEND:
				{
					pThis->OnSend(pTransferData,pData,dwNumberOfByteTransferred);
				}
				break;
			case IO_DATA::OP_RECV:
				{
					pThis->OnRecv(pTransferData,pData,dwNumberOfByteTransferred);
				}
				break;
			case IO_DATA::OP_ACCEPT:
				{
					CStringA msg;
					sockaddr_in *saRemote,*saLocal;
					int namelen=sizeof(sockaddr_in);
					PIO_DATA pListen=pTransferData->GetListenData();
					INT iLocalAddrLen,iRemoteAddrLen;
					
					GetAcceptExSockaddrs(pListen->buf,
						dwNumberOfByteTransferred,
						namelen+16,
						namelen+16,
						(struct sockaddr**)&saLocal,
						&iLocalAddrLen,
						(struct sockaddr**)&saRemote,
						&iRemoteAddrLen);
					//getsockname(pTransferData->GetSocket(),(sockaddr*)&saRemote,&namelen);
					u_short port=ntohs(saRemote->sin_port);
					msg.Format(("Accept From %u %u %s %u\n"),pTransferData->GetSocket(),
						pTransferData->GetActSocket(),
						inet_ntoa(saRemote->sin_addr),
						port);

					OutputDebugStringA(msg);

					LPTransferData lpTransferData=TransferData::NewData();
					pThis->Lock();
					pThis->m_ioMAP[pTransferData->GetActSocket()]=lpTransferData;
					pThis->UnLock();
					lpTransferData->GetSocket()=pTransferData->GetActSocket();
					lpTransferData->GetAddr()=*saRemote;
					HANDLE hIOCP=CreateIoCompletionPort((HANDLE)lpTransferData->GetSocket(),pThis->m_hIOCP,(ULONG_PTR)lpTransferData,0);
					DWORD dwErr=GetLastError();
					lpTransferData->SetRecving();

					ASSERT(hIOCP==pThis->m_hIOCP);
					pThis->Recv(lpTransferData);
					pThis->Accept(pTransferData);
				}
				break;
			default:
				ASSERT(0);
			}
		}
		else
		{
// 			closesocket(pInfo->s);
// 			IO_DATA::DeleteData(pData);
// 			IO_INFO::DeleteInfo(pInfo);
			if(NULL!=pTransferData)
			{
				IOMAP::iterator itr=pThis->m_ioMAP.find(pTransferData->GetSocket());
				if(itr!=pThis->m_ioMAP.end())
				{
					pThis->m_ioMAP.erase(itr);
				}
				closesocket(pTransferData->GetSocket());
				TransferData::DeleteData(pTransferData);
			}
		}
	}
	return 0;
}

VOID CNetBase::OnConnect(LPTransferData lpTransferData,PIO_DATA pData,DWORD dwNumberOfBytesTransferred)
{
	Send(lpTransferData);
	Recv(lpTransferData);
	TRACE(_T("OnConnect %u\n"),lpTransferData->GetSocket());
}

VOID CNetBase::OnRecv(LPTransferData lpTransferData,PIO_DATA pData,DWORD dwNumberOfBytesTransferred)
{
	OutputDebugString(CString((const char*)pData->buf,dwNumberOfBytesTransferred));
	Recv(lpTransferData);
}

VOID CNetBase::OnSend(LPTransferData lpTransferData,PIO_DATA pData,DWORD dwNumberOfBytesTransferred)
{

}
BOOL CNetBase::Send(LPTransferData lpTransferData)
{
	if(NULL!=lpTransferData)
	{
		if((!lpTransferData->IsConnecting()))
		{
			PIO_DATA pSendData=lpTransferData->GetSendData();
			pSendData->UpdateSendBuffer();
			DWORD dwNumberOfBytesTransferred;
			if(0!=pSendData->wsaBuf.len)
			{
				WSASend(lpTransferData->GetSocket(),&pSendData->wsaBuf,1,&dwNumberOfBytesTransferred,0,(LPWSAOVERLAPPED)pSendData,NULL);
				return TRUE;
			}
			else
			{
				return FALSE;
			}		
		}
		else
		{
			return TRUE;
		}
	}
	return  FALSE;
}
BOOL CNetBase::Send(SOCKET sock,LPVOID pSendContent,DWORD dwSendLength)
{
	IOMAP::iterator itr;
	itr=m_ioMAP.find(sock);
	if(itr!=m_ioMAP.end())
	{
		PIO_DATA pSendData=itr->second->GetSendData();
		pSendData->AddContentToSendBuffer(pSendContent,dwSendLength);
		//itr->second.GetSendData().UpdateSendBuffer();
		return Send(itr->second);
	}
	return FALSE;
}

VOID CNetBase::Recv(LPTransferData lpTransferData)
{
	DWORD dwRecv=0;
	DWORD dwFlags=0;
	PIO_DATA pRecvData=lpTransferData->GetRecvData();
	SOCKET sock=lpTransferData->GetSocket();
	pRecvData->wsaBuf.len=MAX_SEND_RECV_BUFFER_SIZE;
	int ret=WSARecv(sock,&pRecvData->wsaBuf,1,&dwRecv,&dwFlags,(LPOVERLAPPED)pRecvData,NULL);
	DWORD dwErr=GetLastError();
}

BOOL CNetBase::Listen( ULONG ulAddr,WORD wPort )
{
// 	WSAPROTOCOL_INFO wsaPI;
// 	INT iProtocol=IPPROTO_TCP;
// 	DWORD dwBufferLength=sizeof(wsaPI);
// 	WSAEnumProtocols(&iProtocol,&wsaPI,&dwBufferLength);
	
	SOCKET sock=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,NULL,WSA_FLAG_OVERLAPPED);
	if(INVALID_SOCKET==sock)
	{
		return FALSE;
	}
	LPTransferData lpTransferData=TransferData::NewData(TRUE);
	m_ioMAP[sock]=lpTransferData;

	sockaddr_in saListenAddr;
	ZeroMemory(&saListenAddr,sizeof(sockaddr_in));
	
	saListenAddr.sin_family=AF_INET;
	saListenAddr.sin_port=htons(wPort);
	saListenAddr.sin_addr.s_addr=ulAddr;
	

	bind(sock,(const sockaddr*)&saListenAddr,sizeof(sockaddr));
	lpTransferData->GetSocket()=sock;
	lpTransferData->GetAddr()=saListenAddr;

	
	HANDLE hIOCP=CreateIoCompletionPort((HANDLE)lpTransferData->GetSocket(),m_hIOCP,(ULONG_PTR)lpTransferData,0);
	DWORD dwErr=GetLastError();

	ASSERT(hIOCP==m_hIOCP);
	
	if(SOCKET_ERROR==listen(sock,SOMAXCONN))
	{
		return INVALID_SOCKET;
	}
	
	return Accept(lpTransferData);

}

BOOL CNetBase::Listen( const char *ulIPAddr,WORD wPort )
{
	return CNetBase::Listen(inet_addr(ulIPAddr),wPort);
}

BOOL CNetBase::Accept( LPTransferData lpTransferData )
{
	DWORD dwNumberOfByteRecv;
	SOCKET actsock=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,NULL,WSA_FLAG_OVERLAPPED);
	lpTransferData->GetActSocket()=actsock;
	return AcceptEx(lpTransferData->GetSocket(),actsock,
		lpTransferData->GetListenData()->buf,
		0,
		//MAX_SEND_RECV_BUFFER_SIZE-((sizeof(SOCKADDR_IN)+16)*2),
		//NULL,
		sizeof(SOCKADDR_IN)+16,
		sizeof(SOCKADDR_IN)+16,
		&dwNumberOfByteRecv,
		&lpTransferData->GetListenData()->ol);;
}

void CNetBase::Lock()
{
	WaitForSingleObject(m_hMutex,INFINITE);
}

void CNetBase::UnLock()
{
	ReleaseMutex(m_hMutex);
}